// Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.

// 客户端会话操作
package clientsession

import (
	server_session "codehub-g.huawei.com/videocloud/mediaprocesscenter/application-gateway/pkg/common"
	"codehub-g.huawei.com/videocloud/mediaprocesscenter/application-gateway/pkg/utils/log"
	"sync"
	"time"
	"fmt"
	"github.com/beego/beego/v2/client/orm"
)

const (
	ClientSessionBackupCountToSleep = 50
	ClientSessionBackupCountToSleepSeconds = 1
)

type ClientSessionDao struct {
	sqlSession orm.Ormer
}

type ClientSessionBackupDao struct {
	sqlSession orm.Ormer
}

// NewClientSessionDao new client session dao
func NewClientSessionDao(sqlSession orm.Ormer) *ClientSessionDao {
	return &ClientSessionDao{sqlSession: sqlSession}
}

// Insert 插入新的数据到数据库表
func (c *ClientSessionDao) Insert(cs *ClientSession) (*ClientSession, error) {
	_, err := c.sqlSession.Insert(cs)
	return cs, err
}
func (c *ClientSessionDao) InsertMulti(num int, css []*ClientSession) ([]*ClientSession, error) {
	_, err := c.sqlSession.InsertMulti(num, css)
	return css, err

}

// Delete  从数据库中client session表删除数据
func (c *ClientSessionDao) Delete(cs *ClientSession) error {
	_, err := c.sqlSession.Delete(cs)
	return err
}

// Update 更新数据库中client session表的数据
func (c *ClientSessionDao) Update(cs *ClientSession) (*ClientSession, error) {

	_, err := c.sqlSession.Update(cs)
	return cs, err
}

// GetClientSessionByID  通过一个client session的id获取一个client session
func (c *ClientSessionDao) GetClientSessionByID(id string) (*ClientSession, error) {
	var cs ClientSession
	cond := orm.NewCondition()
	cond = cond.And("ID", id)
	err := c.sqlSession.QueryTable(&ClientSession{}).SetCond(cond).One(&cs)
	return &cs, err
}

// ListClientSessionByServerSessionID 通过一个Server Session ID获取client session列表
func (c *ClientSessionDao) ListClientSessionByServerSessionID(ID, sort string,
	offset, limit int) (*[]ClientSession, error) {
	var css []ClientSession

	cond := orm.NewCondition()

	if ID != "" {
		cond = cond.And("SERVER_SESSION_ID", ID)
	}

	_, err := c.sqlSession.QueryTable(&ClientSession{}).SetCond(cond).OrderBy(sort).
		Offset(offset).Limit(limit).All(&css)
	return &css, err
}

// ListAllClientSessionsByServerSessionID 通过一个Server Session ID相关的全部 client session
func (c *ClientSessionDao) ListAllClientSessionsByServerSessionID(ID, sort string) (*[]ClientSession, error) {
	var css []ClientSession

	cond := orm.NewCondition()

	if ID != "" {
		cond = cond.And("SERVER_SESSION_ID", ID)
	}

	_, err := c.sqlSession.QueryTable(&ClientSession{}).SetCond(cond).OrderBy(sort).All(&css)
	return &css, err
}

// ListReservedClientSessionForInstance 查询指定节点所有处于状态的server session
func (c *ClientSessionDao) ListReservedClientSessionForInstance(instance string) ([]ClientSession, error) {
	var css []ClientSession
	sqlStr := fmt.Sprintf(`select * from %s where STATE=? and WORK_NODE_ID=? `, TableNameClientSession)
	log.RunLogger.Infof("sqlStr: %v", sqlStr)
	_, err := c.sqlSession.Raw(sqlStr, server_session.ClientSessionStateReserved, instance).QueryRows(&css)
	return css, err

}


// NewClientSessionBackupDao: 创建一个 client session backup dao
func NewClientSessionBackupDao(sqlSession orm.Ormer) *ClientSessionBackupDao {
	return &ClientSessionBackupDao{sqlSession: sqlSession}
}

// InsertClientSessionBackup: 把会话从运行表插入备份表中
func (b *ClientSessionBackupDao) InsertClientSessionBackup(cs *ClientSessionBackup) (*ClientSessionBackup, error) {
	_, err := b.sqlSession.Insert(cs)
	if err != nil {
		log.RunLogger.Errorf("[client session backup service] failed to insert client session %s to backup table error: %+v", 
			cs.ID, err)
		return nil, err
	}
	return cs, nil
}

// DeleteClientSessionBackup: 把会话从备份表中删除
func (b *ClientSessionBackupDao) DeleteClientSessionBackup(ss *ClientSessionBackup) error {
	_, err := b.sqlSession.Delete(ss)
	if err != nil {
		log.RunLogger.Errorf("[cleint session backup service] failed to delete client session %s from backup table error: %+v", 
			ss.ID, err)
		return err
	}
	return nil
}

// QueryClientSessionBackupById: 根据id从备份表中查询数据
func (b *ClientSessionBackupDao) QueryClientSessionBackupById(id string) (*ClientSessionBackup, error) {
	var cs ClientSessionBackup
	cond := orm.NewCondition()
	cond = cond.And(FieldNameClientSessionID, id)
	err := b.sqlSession.QueryTable(&ClientSession{}).SetCond(cond).One(&cs)

	return &cs, err
}

// CleanClientSessionBackup: 根据更新时间从备份表中清理dayBefore天前的数据
func (b *ClientSessionBackupDao) CleanClientSessionBackup(dayBefore int) (error) {
	var css []ClientSessionBackup
	sqlStr := fmt.Sprintf("SELECT * FROM %s WHERE UPDATED_AT < DATE_SUB(NOW(), INTERVAL %d DAY)", 
		TableNameClientSessionBackup, dayBefore)
	toBeCleanCount, err := b.sqlSession.Raw(sqlStr).QueryRows(&css)
	if err != nil {
		log.RunLogger.Errorf("[express data cleaner] query to be clean client session from backup table failed: %+v", err)
		return err
	}
	log.RunLogger.Infof("[express data cleaner] find %v expressed client session from backup table", toBeCleanCount)
	wg := sync.WaitGroup{}
	wg.Add(int(toBeCleanCount))
	for idx, cs := range css {
		if idx % ClientSessionBackupCountToSleep == 0 {
			time.Sleep(ClientSessionBackupCountToSleepSeconds * time.Second)
		}
		go func(client_session ClientSessionBackup) {
			defer wg.Done()
			errD := b.DeleteClientSessionBackup(&client_session)
			if errD != nil {
				log.RunLogger.Errorf("[express data cleaner] delete client session %s from backup table failed: %+v", 
					client_session.ID, errD)
				return
			}
			log.RunLogger.Infof("[express data cleaner] delete client session %s from backup table success", 
				client_session.ID)
		}(cs)
	}
	wg.Wait()
	return nil
}

// BackupClientSession: 定期从运行表中备份残留的会话
func (b *ClientSessionBackupDao) BackupClientSession(dayBefore int) (error) {
	var css []ClientSessionBackup
	sqlStr := fmt.Sprintf("SELECT * FROM %s WHERE UPDATED_AT < DATE_SUB(NOW(), INTERVAL %d DAY)", 
		TableNameClientSession, dayBefore)
	toBeBackupCount, err := b.sqlSession.Raw(sqlStr).QueryRows(&css)
	if err != nil {
		log.RunLogger.Errorf("[express data cleaner] query to be backup client session from runtime table failed: %+v", err)
		return err
	}
	log.RunLogger.Infof("[express data cleaner] find %v to be backup client session from runtime table", toBeBackupCount)
	wg := sync.WaitGroup{}
	wg.Add(int(toBeBackupCount))
	for idx, cs := range css {
		if idx % ClientSessionBackupCountToSleep == 0 {
			time.Sleep(ClientSessionBackupCountToSleepSeconds * time.Second)
		}
		go func(client_session ClientSessionBackup) {
			defer wg.Done()
			_, errI := b.InsertClientSessionBackup(&client_session)
			if errI != nil {
				log.RunLogger.Errorf("[express data cleaner] backup client session %s from runtime table to backup table failed: %+v, " +
					"try to delete it from backup table", client_session.ID, errI)
				backupCs, err := b.QueryClientSessionBackupById(client_session.ID)
				if err != nil {
					log.RunLogger.Errorf("[express data cleaner] query client session %s from backup table failed: %+v", 
					client_session.ID, errI)
					return
				}
				errD := b.DeleteClientSessionBackup(backupCs)
				if errD != nil {
					log.RunLogger.Errorf("[express data cleaner] delete client session %s from backup table failed: %+v", 
					backupCs.ID, errI)
					return
				}
				log.RunLogger.Infof("[express data cleaner] delete client session %s from backup table success", backupCs.ID)
				return
			}
			sqlStrA := fmt.Sprintf("DELETE FROM %s WHERE ID = '%s'", TableNameClientSession, client_session.ID)
			_, errD := b.sqlSession.Raw(sqlStrA).Exec()
			if errD != nil {
				log.RunLogger.Errorf("[express data cleaner] delete terminated client session %s from runtime table error: %+v", client_session.ID, errD)
				return
			}
			log.RunLogger.Infof("[express data cleaner] backup client session %s from runtime table to backup table success", 
				client_session.ID)
		}(cs)
	}
	wg.Wait()
	return nil
}

// 从备份表中备份已被终止的进程
func (b *ClientSessionBackupDao) BackupTerminatedClientSession(Id string, wg *sync.WaitGroup) error {
	defer wg.Done()
	ap := &ClientSessionBackup{}
	sqlStr := fmt.Sprintf("SELECT * FROM %s WHERE ID='%s'", TableNameClientSession, Id)
	err := b.sqlSession.Raw(sqlStr).QueryRow(ap)
	if err != nil {
		log.RunLogger.Errorf("[express data cleaner] query terminated client session %s from runtime table error: %+v", Id, err)
		return err
	}
	// 将待清理数据插入备份表中
	ap.IDInc = 0
	_, err = b.InsertClientSessionBackup(ap)
	if err != nil {
		log.RunLogger.Errorf("[express data cleaner] insert terminated client session %s into backup table error: %+v", Id, err)
		return err
	}
	// 将待清理数据从运行表中删除
	sqlStrA := fmt.Sprintf("DELETE FROM %s WHERE ID = '%s'", TableNameClientSession, Id)
	_, err = b.sqlSession.Raw(sqlStrA).Exec()
	if err != nil {
		log.RunLogger.Errorf("[express data cleaner] delete terminated client session %s from runtime table error: %+v", Id, err)
		return err
	}
	log.RunLogger.Infof("[express data cleaner] success to backup terminated client session %s", Id)
	return nil
}

